home *** CD-ROM | disk | FTP | other *** search
/ La Traviata / La Traviata.iso / viewer / gifmachn.lzh / UnDiff.c < prev   
C/C++ Source or Header  |  1991-07-01  |  17KB  |  712 lines

  1. ; /* "execute undiff.c"
  2. lc -j73 -cfist -L undiff.c
  3. delete undiff.o undiff.lnk
  4. quit
  5. */
  6.  
  7. /*===========================================================================
  8.  * UNDIFF.C -- Apply output from Lattice DIFF
  9.  * Copyright ⌐ 1991 by Robert L. Pyron. All Rights Reserved.
  10.  *
  11.  *    You may use this program for any purpose at all. If you use any
  12.  *    source code from this program, I ask that you give me credit in your
  13.  *    documentation. I would be very pleased if somebody were to make
  14.  *    improvements to this program and pass them on to the Amiga
  15.  *    community.
  16.  * 
  17.  * Lattice DIFF outputs a sequence of commands for converting one text file
  18.  * to another. Unfortunately, there is no program available which will apply
  19.  * these diffs to the original file and yield the modified file. (Or maybe
  20.  * there is, and I just didn't RTFM carefully enough!)
  21.  * 
  22.  * Anyway, this is something I hacked together in about six hours --
  23.  * don't expect production-quality code here.
  24.  *
  25.  * Usage is:
  26.  * 
  27.  *     UNDIFF < diff_file
  28.  * 
  29.  * There are very few bells and whistles here. The only one I can think of
  30.  * offhand is that you may concatenate all of your diff files into one big
  31.  * file, and feed that into UNDIFF.
  32.  * 
  33.  * If the diff file says "TRANSFORM foo/bar.c TO sna/fu.c", then that is
  34.  * what you get -- no redirection of input or output files to somewhere
  35.  * else. 
  36.  * 
  37.  * The destination directory must already exist.
  38.  * 
  39.  * If the program terminates due to an error, you will be left with a
  40.  * malformed target file.
  41.  * 
  42.  * Error output is rather cryptic.
  43.  *===========================================================================
  44.  */
  45.  
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50.  
  51. typedef    unsigned long    ULONG;
  52.  
  53. typedef    enum    _ParseState
  54. {
  55.     IDLE = 0,
  56.     PARSE_TRANSFORM = 10,PARSE_COMMAND,
  57.     DO_APPEND = 20,APPENDING_1,
  58.     DO_CHANGE = 30,CHANGING_1,CHANGING_2,CHANGING_3,CHANGING_4,CHANGING_5,
  59.     DO_DELETE = 40,DELETING_1,DELETING_2,
  60.     DO_NODIFF = 50,
  61.     ERROR = -1,IO_ERROR = -2,
  62.     DONE = 99,QUIT = 100
  63. } ParseState;
  64.  
  65. #define    BADIO    -2
  66. #define    ENDFILE    -1
  67. #define    EMPTY     0
  68. #define    BLANK    ' '
  69. #define    TAB    '\t'
  70. #define    ASTER    '*'
  71. #define    NODIFF    'N'
  72. #define    DASH    '-'
  73. #define    SRCLINE    '<'
  74. #define    DSTLINE    '>'
  75.  
  76. char    srcname[256];    /* name of current source file */
  77. char    dstname[256];    /* name of current destination file */
  78.  
  79. char    srctemp[256];    /* temp; src file specified in this command */
  80. char    dsttemp[256];    /* temp; dst file specified in this command */
  81.  
  82. char    srcrange[32];    /* temp for decoding range specification */
  83. char    dstrange[32];    /* temp for decoding range specification */
  84.  
  85. char    cmdbuf[256];    /* input buffer for dif file */
  86. char    linebuf[256];    /* buffer for moving lines from file A to file B */
  87.  
  88. ULONG    src1, src2;    /* source line range specified by command */
  89. ULONG    dst1, dst2;    /* dest line range specified by command */
  90.  
  91. ULONG    cmdline;    /* current line number in dif file */
  92. ULONG    srcline;    /* current line number in source file */
  93. ULONG    dstline;    /* current line number in destination file */
  94.  
  95. FILE    *cmdfp;
  96. FILE    *srcfp;
  97. FILE    *dstfp;
  98.  
  99. void main (int argc, char **argv);
  100. int parse_transform (char *cmdbuf);
  101. int parse_nodiff (char *cmdbuf);
  102. int parse_append (char *cmdbuf);
  103. int parse_change (char *cmdbuf);
  104. int parse_delete (char *cmdbuf);
  105. int parse_range (char *range, unsigned long *lo, unsigned long *hi);
  106. int getdif (char *cmdbuf);
  107. int getsrc (char *linebuf);
  108. int putdst (char *linebuf);
  109.  
  110. /*-------------------------------------------------------------------------
  111.  *-------------------------------------------------------------------------
  112.  */
  113.  
  114. void    main (int argc, char **argv)
  115. {
  116.     ParseState    state = IDLE, laststate;
  117.     char        *error = "";
  118.     int        cmd, rc;
  119.  
  120.     cmdfp = stdin;    /****** LAZY LAZY LAZY ******/
  121.  
  122. loop:
  123.     switch (state)
  124.     {
  125.     case IDLE:
  126.         laststate = state;
  127.  
  128.         /*
  129.          * We're just waiting around for something to happen.
  130.          */
  131.         error = "unknown command";
  132.         switch (cmd = getdif (cmdbuf))
  133.         {
  134.         case BADIO:    state = IO_ERROR;    break;
  135.         case ENDFILE:    state = DONE;        break;
  136.         case EMPTY:    state = IDLE;        break;
  137.         case BLANK:    state = PARSE_TRANSFORM; break;
  138.         case TAB:    state = PARSE_TRANSFORM; break;
  139.         case ASTER:    state = PARSE_COMMAND;    break;
  140.         case NODIFF:    state = PARSE_COMMAND;    break;
  141.         default:    state = ERROR;        break;
  142.         }
  143.         break;
  144.  
  145.     case PARSE_TRANSFORM:
  146.         laststate = state;
  147.  
  148.         /*
  149.          * Copy remainder of current file.
  150.          */
  151.         if (srcfp && dstfp)
  152.         {
  153.             do
  154.             {
  155.                 if ((rc = getsrc(linebuf)) == 0)
  156.                     rc = putdst(linebuf);
  157.             }
  158.             while (rc == 0);
  159.             if (ferror(srcfp) || ferror(dstfp))
  160.                 { state = IO_ERROR; break; }
  161.         }
  162.  
  163.         /*
  164.          * Confirm that cmdbuf contains a "TO TRANSFORM" command.
  165.          * Close current src and dst files, and open new files.
  166.          */
  167.         error = "bad command";
  168.         if (parse_transform(cmdbuf))
  169.             { state = ERROR; break; }
  170.  
  171.         printf ("TRANSFORMING %s TO %s ...\n",
  172.             srcname, dstname);
  173.  
  174.         if (srcfp) { fclose (srcfp); srcfp = NULL; }
  175.         if (dstfp) { fclose (dstfp); dstfp = NULL; }
  176.  
  177.         srcfp = fopen (srcname,"r");
  178.         dstfp = fopen (dstname,"w");
  179.         srcline = dstline = 0;
  180.  
  181.         state = (srcfp && dstfp) ? IDLE : IO_ERROR;
  182.         break;
  183.  
  184.     case PARSE_COMMAND:
  185.         laststate = state;
  186.  
  187.         /*
  188.          * cmdbuf should contain an APPEND, CHANGE, or DELETE command.
  189.          */
  190.         error = "bad command";
  191.         if (parse_append(cmdbuf) == 0)
  192.             state = DO_APPEND;
  193.         else if (parse_change(cmdbuf) == 0)
  194.             state = DO_CHANGE;
  195.         else if (parse_delete(cmdbuf) == 0)
  196.             state = DO_DELETE;
  197.         else if (parse_nodiff(cmdbuf) == 0)
  198.             state = DO_NODIFF;
  199.         else
  200.             state = ERROR;
  201.         break;
  202.  
  203.     case DO_NODIFF:
  204.         laststate = state;
  205.  
  206.         error = "inconsistent file name";
  207.         if (stricmp(srcname,srctemp) || stricmp(dstname,dsttemp))
  208.             { state = ERROR; break; }
  209.  
  210.         /*
  211.          * Copy remainder of file.
  212.          */
  213.         if (srcfp && dstfp)
  214.         {
  215.             do
  216.             {
  217.                 if ((rc = getsrc(linebuf)) == 0)
  218.                     rc = putdst(linebuf);
  219.             }
  220.             while (rc == 0);
  221.             if (ferror(srcfp) || ferror(dstfp))
  222.                 { state = IO_ERROR; break; }
  223.         }
  224.  
  225.         state = IDLE;
  226.         break;
  227.  
  228.     case DO_APPEND:
  229.         laststate = state;
  230.  
  231.         /*
  232.          * cmdbuf contains an APPEND command.
  233.          * Make sure file name agrees.
  234.          */
  235.         error = "inconsistent file name";
  236.         if (stricmp(srcname,srctemp))
  237.             { state = ERROR; break; }
  238.  
  239.         /*
  240.          * Copy unaffected lines.
  241.          */
  242.         while (srcline < src1)
  243.         {
  244.             if (getsrc(linebuf) != 0)
  245.                 { state = IO_ERROR; break; }
  246.             if (putdst(linebuf) != 0)
  247.                 { state = IO_ERROR; break; }
  248.         }
  249.         error = "inconsistent line count";
  250.         if (srcline != src1)
  251.             { state = ERROR; break; }
  252.  
  253.         /*
  254.          * Get next dif line. Should begin with '>'.
  255.          */
  256.         error = "bad command";
  257.         switch (cmd = getdif (cmdbuf))
  258.         {
  259.         case BADIO:    state = IO_ERROR;    break;
  260.         case DSTLINE:    state = APPENDING_1;    break;
  261.         default:    state = ERROR;        break;
  262.         }
  263.         break;
  264.  
  265.     case APPENDING_1:
  266.         laststate = state;
  267.  
  268.         /*
  269.          * cmdbuf contains a line that should be output.
  270.          * Do so, then get next dif line.
  271.          */
  272.         if (putdst(cmdbuf+1) != 0)
  273.             { state = IO_ERROR; break; }
  274.  
  275.         error = "bad command";
  276.         switch (cmd = getdif (cmdbuf))
  277.         {
  278.         case BADIO:    state = IO_ERROR;    break;
  279.         case ENDFILE:    state = DONE;        break;
  280.         case EMPTY:    state = IDLE;        break;
  281.         case DSTLINE:    state = APPENDING_1;    break;
  282.         default:    state = ERROR;        break;
  283.         }
  284.         break;
  285.  
  286.     case DO_CHANGE:
  287.         laststate = state;
  288.  
  289.         /*
  290.          * cmdbuf contains a CHANGE command.
  291.          * Make sure file names agree.
  292.          */
  293.         error = "inconsistent file name";
  294.         if (stricmp(srcname,srctemp) || stricmp(dstname,dsttemp))
  295.             { state = ERROR; break; }
  296.  
  297.         /*
  298.          * Copy unaffected lines.
  299.          */
  300.         while (srcline < src1-1 && dstline < dst1-1)
  301.         {
  302.             if (getsrc(linebuf) != 0)
  303.                 { state = IO_ERROR; break; }
  304.             if (putdst(linebuf) != 0)
  305.                 { state = IO_ERROR; break; }
  306.         }
  307.         error = "inconsistent line count";
  308.         if (srcline != src1-1 || dstline != dst1-1)
  309.             { state = ERROR; break; }
  310.  
  311.         /*
  312.          * Get next dif line. Should begin with '<'.
  313.          */
  314.         error = "bad command";
  315.         switch (cmd = getdif (cmdbuf))
  316.         {
  317.         case BADIO:    state = IO_ERROR;    break;
  318.         case SRCLINE:    state = CHANGING_1;    break;
  319.         default:    state = ERROR;        break;
  320.         }
  321.         break;
  322.  
  323.     case CHANGING_1:
  324.         laststate = state;
  325.  
  326.         /*
  327.          * cmdbuf contains a line that should be "deleted".
  328.          * Make sure it matches current line in src file.
  329.          */
  330.         if (getsrc(linebuf) != 0)
  331.             { state = IO_ERROR; break; }
  332.         error = "line does not match";
  333.         if (strcmp(linebuf,cmdbuf+1))
  334.             { state = ERROR; break; }
  335.  
  336.         /*
  337.          * Get next dif line.
  338.          */
  339.         error = "bad command";
  340.         switch (cmd = getdif (cmdbuf))
  341.         {
  342.         case BADIO:    state = IO_ERROR;    break;
  343.         case SRCLINE:    state = CHANGING_1;    break;
  344.         case EMPTY:    state = CHANGING_2;    break;
  345.         default:    state = ERROR;        break;
  346.         }
  347.         break;
  348.  
  349.     case CHANGING_2:
  350.         laststate = state;
  351.  
  352.         /*
  353.          * We've finished the "delete" component of the change.
  354.          * Look for separator line between "delete" and "append"
  355.          * lines.
  356.          */
  357.         error = "inconsistent line count";
  358.         if (srcline != src2)
  359.             { state = ERROR; break; }
  360.  
  361.         error = "bad command";
  362.         switch (cmd = getdif (cmdbuf))
  363.         {
  364.         case BADIO:    state = IO_ERROR;    break;
  365.         case EMPTY:    state = CHANGING_2;    break;
  366.         case DASH:    state = CHANGING_3;    break;
  367.         default:    state = ERROR;        break;
  368.         }
  369.         break;
  370.  
  371.     case CHANGING_3:
  372.         laststate = state;
  373.  
  374.         /*
  375.          * cmdbuf holds separator between "delete and "append" lines.
  376.          * Next line should begin with '>'.
  377.          */
  378.         error = "bad command";
  379.         switch (cmd = getdif (cmdbuf))
  380.         {
  381.         case BADIO:    state = IO_ERROR;    break;
  382.         case DSTLINE:    state = CHANGING_4;    break;
  383.         default:    state = ERROR;        break;
  384.         }
  385.         break;
  386.  
  387.     case CHANGING_4:
  388.         laststate = state;
  389.  
  390.         /*
  391.          * cmdbuf contains a line that should be output.
  392.          * Do so, then get next dif line.
  393.          */
  394.  
  395.         if (putdst(cmdbuf+1) != 0)
  396.             { state = IO_ERROR; break; }
  397.  
  398.         error = "bad command";
  399.         switch (cmd = getdif (cmdbuf))
  400.         {
  401.         case BADIO:    state = IO_ERROR;    break;
  402.         case DSTLINE:    state = CHANGING_4;    break;
  403.         case EMPTY:    state = IDLE;        break;
  404.         default:    state = ERROR;        break;
  405.         }
  406.         break;
  407.  
  408.     case CHANGING_5:
  409.         laststate = state;
  410.  
  411.         /*
  412.          * Check to see that we inserted the proper number of lines.
  413.          */
  414.         error = "inconsistent line count";
  415.         state = (dstline == dst2) ? IDLE : ERROR;
  416.         break;
  417.  
  418.  
  419.     case DO_DELETE:
  420.         laststate = state;
  421.  
  422.         /*
  423.          * cmdbuf contains an APPEND command.
  424.          * Make sure file name agrees.
  425.          */
  426.         error = "inconsistent file name";
  427.         if (stricmp(srcname,srctemp))
  428.             { state = ERROR; break; }
  429.  
  430.         /*
  431.          * Copy unaffected lines.
  432.          */
  433.         while (srcline < src1-1)
  434.         {
  435.             if (getsrc(linebuf) != 0)
  436.                 { state = IO_ERROR; break; }
  437.             if (putdst(linebuf) != 0)
  438.                 { state = IO_ERROR; break; }
  439.         }
  440.         error = "inconsistent line count";
  441.         if (srcline != src1-1)
  442.             { state = ERROR; break; }
  443.  
  444.         /*
  445.          * Get next dif line. Should begin with '<'.
  446.          */
  447.         error = "bad command";
  448.         switch (cmd = getdif (cmdbuf))
  449.         {
  450.         case BADIO:    state = IO_ERROR;    break;
  451.         case SRCLINE:    state = DELETING_1;    break;
  452.         default:    state = ERROR;        break;
  453.         }
  454.         break;
  455.  
  456.     case DELETING_1:
  457.         laststate = state;
  458.  
  459.         /*
  460.          * cmdbuf contains a line that should be "deleted".
  461.          * Make sure it matches current line in src file.
  462.          */
  463.         if (getsrc(linebuf) != 0)
  464.             { state = IO_ERROR; break; }
  465.         error = "line does not match";
  466.         if (strcmp(linebuf,cmdbuf+1))
  467.             { state = ERROR; break; }
  468.  
  469.         /*
  470.          * Get next dif line.
  471.          */
  472.         error = "bad command";
  473.         switch (cmd = getdif (cmdbuf))
  474.         {
  475.         case BADIO:    state = IO_ERROR;    break;
  476.         case SRCLINE:    state = DELETING_1;    break;
  477.         case EMPTY:    state = DELETING_2;    break;
  478.         default:    state = ERROR;        break;
  479.         }
  480.         break;
  481.  
  482.     case DELETING_2:
  483.         laststate = state;
  484.  
  485.         /*
  486.          * Check to see that we deleted the proper number of lines.
  487.          */
  488.         error = "inconsistent line count";
  489.         state = (srcline == src2) ? IDLE : ERROR;
  490.         break;
  491.  
  492.     case ERROR:
  493.         printf ("*** ERROR: %s\n", error);
  494.         printf ("state = %d  cmdline = %d  srcline = %d  dstline = %d\n",
  495.             (int) laststate, cmdline, srcline, dstline);
  496.         printf ("src1 = %d src2 = %d  dst1 = %d dst2 = %d\n",
  497.             src1, src2, dst1, dst2);
  498.         printf ("cmdbuf  = %s\n", cmdbuf);
  499.         printf ("linebuf = %s\n", linebuf);
  500.         printf ("srcrange = %s\n", srcrange);
  501.         printf ("dstrange = %s\n", dstrange);
  502.  
  503.         rc = 100;
  504.         state = QUIT;
  505.         break;
  506.  
  507.     case IO_ERROR:
  508.         printf ("*** I/O error\n");
  509.         rc = 200;
  510.         state = QUIT;
  511.         break;
  512.  
  513.     case DONE:
  514.         /*
  515.          * Copy remainder of file.
  516.          */
  517.         if (srcfp && dstfp)
  518.         {
  519.             do
  520.             {
  521.                 if ((rc = getsrc(linebuf)) == 0)
  522.                     rc = putdst(linebuf);
  523.             }
  524.             while (rc == 0);
  525.             if (ferror(srcfp) || ferror(dstfp))
  526.                 { state = IO_ERROR; break; }
  527.         }
  528.  
  529.         rc = 0;
  530.         state = QUIT;
  531.         break;
  532.  
  533.     case QUIT:
  534.         if (srcfp) { fclose (srcfp); srcfp = NULL; }
  535.         if (dstfp) { fclose (dstfp); dstfp = NULL; }
  536.         exit(rc);
  537.         break;
  538.  
  539.     default:
  540.         error = "unknown state!";
  541.         state = ERROR;
  542.         break;
  543.     }
  544.     goto loop;
  545. }
  546.  
  547.  
  548. /*-------------------------------------------------------------------------
  549.  * Extract srcname and dstname from TRANSFORM command.
  550.  *    "TO TRANSFORM <srcname> INTO <dstname> ..."
  551.  *
  552.  * Sets global variables "srcname" and "dstname".
  553.  * Returns 0 on success.
  554.  *-------------------------------------------------------------------------
  555.  */
  556.  
  557. int    parse_transform (char *cmdbuf)
  558. {
  559.     int nc = sscanf (cmdbuf, " TO TRANSFORM %s INTO %s ...",
  560.             srcname, dstname);
  561.     return (nc != 2);
  562. }
  563.  
  564. /*-------------------------------------------------------------------------
  565.  * Extract srcname and dstname from NODIFF command.
  566.  *    "NO DIFFERENCE BETWEEN <dst> AND <src>"
  567.  *
  568.  * Sets global variables "srctemp" and "dsttemp".
  569.  * Returns 0 on success.
  570.  *-------------------------------------------------------------------------
  571.  */
  572.  
  573. int    parse_nodiff (char *cmdbuf)
  574. {
  575.     int nc = sscanf (cmdbuf, "NO DIFFERENCE BETWEEN %s AND %s",
  576.             dsttemp, srctemp);
  577.     return (nc != 2);
  578. }
  579.  
  580. /*-------------------------------------------------------------------------
  581.  * Extract line number and file name from APPEND command.
  582.  *    "*** APPEND AFTER <n> IN <dst> ***"
  583.  *
  584.  * Returns 0 on success.
  585.  *-------------------------------------------------------------------------
  586.  */
  587.  
  588. int    parse_append (char *cmdbuf)
  589. {
  590.     int nc = sscanf (cmdbuf, "*** APPEND AFTER %s IN %s ***",
  591.             srcrange, srctemp);
  592.  
  593.     return (nc == 2) ? parse_range(srcrange,&src1,&src2) : -1;
  594. }
  595.  
  596. /*-------------------------------------------------------------------------
  597.  * Extract info from CHANGE command.
  598.  *    "*** CHANGE <range> IN <src> TO <range> IN <dst> ***",
  599.  *
  600.  * Returns 0 on success.
  601.  *-------------------------------------------------------------------------
  602.  */
  603.  
  604. int    parse_change (char *cmdbuf)
  605. {
  606.     int nc = sscanf (cmdbuf, "*** CHANGE %s IN %s TO %s IN %s ***",
  607.             srcrange, srctemp, dstrange, dsttemp);
  608.     if (nc != 4)
  609.         return -1;
  610.     else if (parse_range(srcrange,&src1,&src2) != 0)
  611.         return -1;
  612.     else if (parse_range(dstrange,&dst1,&dst2) != 0)
  613.         return -1;
  614.     return 0;
  615. }
  616.  
  617. /*-------------------------------------------------------------------------
  618.  * Extract line number and file name from DELETE command.
  619.  *    "*** DELETE <range> FROM <src> ***",
  620.  *
  621.  * Returns 0 on success.
  622.  *-------------------------------------------------------------------------
  623.  */
  624.  
  625. int    parse_delete (char *cmdbuf)
  626. {
  627.     int nc = sscanf (cmdbuf, "*** DELETE %s FROM %s ***",
  628.             srcrange, srctemp);
  629.  
  630.     return (nc == 2) ? parse_range(srcrange,&src1,&src2) : -1;
  631. }
  632.  
  633. /*-------------------------------------------------------------------------
  634.  * Parse a range specifier in the form "n" or "[n,n]".
  635.  * Returns 0 on success.
  636.  *-------------------------------------------------------------------------
  637.  */
  638.  
  639. int    parse_range (char *range, ULONG *lo, ULONG *hi)
  640. {
  641.     *lo = *hi = -1;
  642.     if (sscanf (range,"[%lu,%lu]", lo, hi) == 2)
  643.         return 0;
  644.     else if (sscanf (range,"%lu", lo) == 1)
  645.     {
  646.         *hi = *lo;
  647.         return 0;
  648.     }
  649.     else
  650.     {
  651.         printf ("****** cannot scan range %s ******\n", range);
  652.         return -1;
  653.     }
  654. }
  655.  
  656. /*-------------------------------------------------------------------------
  657.  * Read line from dif file. Strip trailing newline.
  658.  * Return first character (or negative value for error or eof).
  659.  *-------------------------------------------------------------------------
  660.  */
  661.  
  662. int    getdif (char *cmdbuf)
  663. {
  664.     int    len;
  665.  
  666.     if (fgets(cmdbuf,255,cmdfp))
  667.     {
  668.         cmdbuf[255] = '\0';
  669.         len = strlen(cmdbuf);
  670.         if (len && cmdbuf[--len] == '\n')
  671.             cmdbuf[len] = '\0';
  672.         cmdline++;
  673.         return (int) cmdbuf[0];
  674.     }
  675.     else if (feof(cmdfp))
  676.         return ENDFILE;
  677.     else
  678.         return BADIO;
  679. }
  680.  
  681. /*-------------------------------------------------------------------------
  682.  * Read line from source file; increment counter.
  683.  *-------------------------------------------------------------------------
  684.  */
  685.  
  686. int    getsrc (char *linebuf)
  687. {
  688.     char *p;
  689.     int len;
  690.     p = fgets(linebuf,255,srcfp);
  691.     if (p && (len = strlen(p)) && (p[--len] == '\n'))
  692.         p[len] = '\0';
  693.  
  694.     linebuf[255] = '\0';
  695.     srcline++;
  696.     return (p == NULL);
  697. }
  698.  
  699. /*-------------------------------------------------------------------------
  700.  * Write line to destination file; increment counter.
  701.  *-------------------------------------------------------------------------
  702.  */
  703.  
  704. int    putdst (char *linebuf)
  705. {
  706.     int rc = fputs (linebuf,dstfp);
  707.     fputc ('\n',dstfp);
  708.     dstline++;
  709.     return rc;
  710. }
  711.  
  712.